﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using System.Text.RegularExpressions;

namespace GeneratorLibrary.Client.Base {
    public abstract class ClassBase {
        string isActiveColumn = "IsActive";

        public string IsActiveColumn {
            get { return isActiveColumn ?? ""; }
            set { isActiveColumn = value ?? ""; }
        }

        protected List<ClassColumnBase> columns;
        protected StringCodeBuilder classBuilder;
        bool allowsIDataModel = false;

        protected string dacName;

        public ClassBase(DataTable dt, string table, string classNamespace, string dacNamespace, string dacName) {
            this.dacName = dacName;
            columns = new List<ClassColumnBase>(dt.Rows.Count);
            foreach (DataRow row in dt.Rows) {
                ClassColumnBase column = CreateColumn(row);
                columns.Add(column);
                if (column.AllowsIDataModel)
                    allowsIDataModel = true;
            }
            generate(classNamespace, dacNamespace, table);
        }
        private ClassBase() { }

        protected abstract ClassColumnBase CreateColumn(DataRow row);
        protected abstract string GetExtraUsingStatements();

        private void generate(string classNamespace, string dacNamespace, string table) {
            classBuilder = new StringCodeBuilder();

            GenerateUsingStatements(classNamespace, dacNamespace);
            classBuilder.Append("\n");
            GenerateNamespace(classNamespace, table);
        }

        private void GenerateUsingStatements(string classNamespace, string dacNamespace) {
            classBuilder.Append("using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing StandardDAC;\n");
            classBuilder.Append(GetExtraUsingStatements());
            if (classNamespace != dacNamespace && !String.IsNullOrEmpty(dacNamespace))
                classBuilder.Append("using " + dacNamespace + ";\n");

            classBuilder.Append("\n");
        }

        private void GenerateNamespace(string classNamespace, string table) {
            if (!String.IsNullOrEmpty(classNamespace))
                classBuilder.AppendBeginAfter("namespace " + classNamespace + " {\n");

            GenerateClass(classNamespace, table);

            if (!String.IsNullOrEmpty(classNamespace))
                classBuilder.AppendEndBefore("}\n");
        }

        private void GenerateClass(string classNamespace, string table) {
            if (allowsIDataModel)
                classBuilder.AppendBeginAfter("public class " + table + " : IDataModel {\n");
            else
                classBuilder.AppendBeginAfter("public class " + table + " {\n");
            
            GenerateMembers();
            GenerateProperties();
            GenerateConstructors(table);
            GenerateDACMethods(table);

            foreach (KeyValuePair<string, string> item in ExtraRegions()) {
                classBuilder.Append("#region " + item.Key + "\n");
                classBuilder.Append(item.Value + "\n");
                classBuilder.Append("#endregion " + item.Key + "\n");
            }

            classBuilder.AppendEndBefore("}\n");
        }
        private void GenerateDACMethods(string table) {
            classBuilder.Append("#region DAC Methods\n");
            classBuilder.Append("/// <summary>\n///Reads all items in database.\n/// </summary>\n");
            classBuilder.AppendBeginAfter("public static List<" + table + "> ReadAll() {\n");
            classBuilder.Append(ReadAll(table));
            classBuilder.AppendEndBefore("}\n");

            if (allowsIDataModel) {
                classBuilder.Append("/// <summary>\n///Populates item from database by its id.\n/// </summary>\n");
                classBuilder.AppendBeginAfter("private void populate() {\n"); 
                classBuilder.Append(ReadById(table));
                classBuilder.AppendEndBefore("}\n");
            }
            classBuilder.Append("/// <summary>\n///Saves item to database.\n/// </summary>\n");
            classBuilder.AppendBeginAfter("public void Save() {\n");
            classBuilder.Append(dacName + ".Save(this, \"" + table + "\");\n");
            classBuilder.AppendEndBefore("}\n");

            classBuilder.Append("/// <summary>\n///Deletes item from database.\n/// </summary>\n");
            classBuilder.AppendBeginAfter("public void Delete() {\n");
            classBuilder.Append(dacName + ".Delete(this, \"" + table + "\");\n");
            classBuilder.AppendEndBefore("}\n");

            foreach (ClassColumnBase c in columns) {
                string comment = null;

                if (c.IsForeignKey)
                    comment = "Read By Foreign Key";
                else if ((!allowsIDataModel || !c.IsPrimaryKey) && c.IsUniqueConstraint)
                    comment = "Read by Unique Constraint";
                else if (c.ColumnName.ToLower() == IsActiveColumn.ToLower())
                    comment = "Read by IsActive Flag";
                else
                    continue;

                if (c.DataType == "bool?") {
                    classBuilder.Append("/// <summary>\n///" + comment + "\n/// </summary>\n/// <param name=\"in" + c.ColumnName + "\">0: FALSE, 1: TRUE, 2:NULL</param>\n");
                    classBuilder.AppendBeginAfter("public static List<" + table + "> ReadBy" + c.ColumnName + "(" + c.DataType + " in" + c.ColumnName + ") {");
                    classBuilder.Append("object parameter = in" + c.ColumnName + ".HasValue ? (in" + c.ColumnName + ".Value ? 1 : 0) : 2);\n");

                    classBuilder.Append(ReadByParams(table, c, "parameter"));

                    classBuilder.AppendEndBefore("}\n");
                } else if (c.IsNullable) {
                    classBuilder.Append("/// <summary>\n///" + comment + "\n/// </summary>\n/// <param name=\"in" + c.ColumnName + "\"></param>\n");
                    classBuilder.AppendBeginAfter("public static List<" + table + "> ReadBy" + c.ColumnName + "(" + c.DataType + " in" + c.ColumnName + ") {\n");

                    if (c.NullValue != "null") {
                        classBuilder.Append("object parameter = in" + c.ColumnName + " ?? " + c.NullValue + ";\n");
                        classBuilder.Append(ReadByParams(table, c, "parameter"));
                    } else {
                        string tempNull = c.GetDotNetDefaultCoalesce();
                        if (!String.IsNullOrEmpty(tempNull)) {
                            classBuilder.Append("object parameter = in" + c.ColumnName + " ?? " + c.GetDotNetDefaultCoalesce() + ";\n");
                            classBuilder.Append(ReadByParams(table, c, "parameter"));
                        } else {
                            classBuilder.Append(ReadByParams(table, c, "in" + c.ColumnName));
                        }
                    }
                    classBuilder.AppendEndBefore("}\n");
                } else {
                    classBuilder.Append("/// <summary>\n///" + comment + "\n/// </summary>\n/// <param name=\"in" + c.ColumnName + "\"></param>\n");
                    classBuilder.AppendBeginAfter("public static List<" + table + "> ReadBy" + c.ColumnName + "(" + c.DataType + " in" + c.ColumnName + ") {\n");
                    classBuilder.Append(ReadByParams(table, c, "in" + c.ColumnName));
                    classBuilder.AppendEndBefore("}\n");
                }
            }
            classBuilder.Append(ExtraDACMethods());
            classBuilder.Append("#endregion DAC Methods\n");
        }

        protected abstract string ReadAll(string table);
        protected abstract string ReadById(string table);
        protected abstract string ReadByParams(string table, ClassColumnBase c, string parameterVariable);

        protected virtual string ExtraMembers() {
            return "";
        }
        protected virtual string ExtraProperties() {
            return "";
        }
        protected virtual string ExtraDACMethods() {
            return "";
        }
        protected virtual string ExtraConstructors() {
            return "";
        }
        protected virtual List<KeyValuePair<string,string>> ExtraRegions() {
            return new List<KeyValuePair<string,string>>();
        }


        private void GenerateConstructors(string table) {
            classBuilder.Append("#region Constructors\n");

            //default constructor
            classBuilder.Append("/// <summary>\n///Default Constructor. Required by DAL Helper\n/// </summary>\n");
            classBuilder.Append("public " + table + "() { }\n");

            if (allowsIDataModel) {
                //populateById constructor
                classBuilder.Append("/// <summary>\n///Gets item by Id.\n/// </summary>\n/// <param name=\"inId\"></param>\n");
                classBuilder.AppendBeginAfter("public " + table + "(int inId) {\n");
                classBuilder.Append("this.id = inId;\npopulate();\n");
                classBuilder.AppendEndBefore("}\n");
            }
            classBuilder.Append("\n");
            classBuilder.Append(ExtraConstructors());
            classBuilder.Append("#endregion Constructors\n");
        }

        private void GenerateProperties() {
            List<string> propertyMethods = new List<string>();
            classBuilder.Append("#region Properties\n");
            foreach (ClassColumnBase c in columns) {
                classBuilder.AppendBraces(c.DacAttribute);
                classBuilder.AppendBraces(c.Property);
                if (!String.IsNullOrEmpty(c.ExtraMethod))
                    propertyMethods.Add(c.ExtraMethod);
            }
            classBuilder.Append(ExtraProperties());
            classBuilder.Append("\n#endregion Properties\n");

            if (propertyMethods.Count > 0) {
                classBuilder.Append("#region IsNew()\n");
                foreach (string s in propertyMethods) {
                    classBuilder.AppendBraces(s);
                }
                classBuilder.Append("\n#endregion IsNew()\n");
            }
        }

        private void GenerateMembers() {
            classBuilder.Append("#region members\n");
            foreach (ClassColumnBase c in columns) {
                classBuilder.Append(c.Member);
            }
            classBuilder.Append(ExtraMembers());
            classBuilder.Append("\n#endregion members\n");
        }
        public void WriteNewFile(string filepath) {
            FileInfo f = new FileInfo(filepath);
            if (!f.Exists || f.Length == 0) {
                using (StreamWriter w = new StreamWriter(filepath)) {
                    w.Write(classBuilder.ToString().Replace("\n", Environment.NewLine));
                }
            }
        }
        public string WriteString() {
            return classBuilder.ToString();
        }
    }
}
